package com.vhans.db.service.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vhans.core.domain.BAgreementUser;
import com.vhans.core.domain.BUser;
import com.vhans.core.domain.BUserCollect;
import com.vhans.core.domain.BUserLike;
import com.vhans.core.enums.FilePathEnum;
import com.vhans.core.redis.RedisService;
import com.vhans.core.strategy.context.UploadStrategyContext;
import com.vhans.core.utils.BeanCopyUtils;
import com.vhans.core.utils.SecurityUtils;
import com.vhans.core.utils.StringUtils;
import com.vhans.core.web.model.dto.AgreementDTO;
import com.vhans.db.mapper.*;
import com.vhans.db.model.dto.ConditionDTO;
import com.vhans.db.model.dto.DisableDTO;
import com.vhans.db.model.dto.EmailDTO;
import com.vhans.db.model.dto.user.UserDTO;
import com.vhans.db.model.dto.user.UserInfoDTO;
import com.vhans.db.model.dto.user.UserPasswdDTO;
import com.vhans.db.model.vo.agreement.AgreementAllVO;
import com.vhans.db.model.vo.agreement.AgreementInfoAllVO;
import com.vhans.db.model.vo.agreement.MyAgreement;
import com.vhans.db.model.vo.agreement.MyIssueAgreement;
import com.vhans.db.model.vo.comment.CommentVO;
import com.vhans.db.model.vo.quiz.QuizInfoVO;
import com.vhans.db.model.vo.quiz.QuizVO;
import com.vhans.db.model.vo.record.RecordInfoVO;
import com.vhans.db.model.vo.record.RecordVO;
import com.vhans.db.model.vo.user.OnlineUserVO;
import com.vhans.db.model.vo.user.UserInfoVO;
import com.vhans.db.model.vo.user.UserVO;
import com.vhans.db.service.IBAgreementService;
import com.vhans.db.service.IBQuizService;
import com.vhans.db.service.IBRecordService;
import com.vhans.db.service.IBUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

import static com.vhans.core.constant.CommonConstant.ONLINE_USER;
import static com.vhans.core.constant.CommonConstant.TRUE;
import static com.vhans.core.constant.RedisConstant.*;
import static com.vhans.core.enums.AgreementTypeEnum.*;
import static com.vhans.core.enums.CommentTypeEnum.*;

/**
 * 用户业务接口实现类
 *
 * @author vhans.db
 * @date 2022/12/05 15:32
 **/
@Service
public class BUserServiceImpl extends ServiceImpl<BUserMapper, BUser> implements IBUserService {

    @Autowired
    private BUserMapper userMapper;

    @Autowired
    private BAgreementUserMapper agreementUserMapper;

    @Autowired
    private BUserCollectMapper userCollectMapper;

    @Autowired
    private BUserLikeMapper userLikeMapper;

    @Autowired
    private BCommentMapper commentMapper;

    @Autowired
    private IBAgreementService agreementService;

    @Autowired
    private IBRecordService recordService;

    @Autowired
    private IBQuizService quizService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private UploadStrategyContext uploadStrategyContext;

    @Override
    public List<UserVO> listUser(ConditionDTO condition) {
        return userMapper.listUserVO(condition);
    }

    @Override
    public UserInfoVO getUserInfo() {
        Integer userId = StpUtil.getLoginIdAsInt();
        BUser user = userMapper.selectOne(new LambdaQueryWrapper<BUser>()
                .select(BUser::getAvatar, BUser::getNickname, BUser::getEmail,
                        BUser::getUsername, BUser::getDegree, BUser::getGrade,
                        BUser::getIntro, BUser::getLoginType)
                .eq(BUser::getId, userId));
        // 点赞记录
        List<Integer> recordIds = getLikeRecordIds(userId);
        // 点赞评论
        List<Integer> quizIds = getLikeQuizIds(userId);
        // 点赞题目
        List<Integer> commentIds = getLikeCommentIds(userId);
        // 用户参与的约起记录
        List<MyIssueAgreement> myIssueAgreements = getIssueAgreement(userId);
        return UserInfoVO
                .builder()
                .id(userId)
                .avatar(user.getAvatar())
                .nickname(user.getNickname())
                .username(user.getUsername())
                .email(user.getEmail())
                .intro(user.getIntro())
                .degree(user.getDegree())
                .grade(user.getGrade())
                .recordLikeSet(recordIds)
                .quitLikeSet(quizIds)
                .commentLikeSet(commentIds)
                .agreeIssueSet(myIssueAgreements)
                .loginType(user.getLoginType())
                .build();
    }

    @Override
    public List<MyAgreement> getIssueAgreement() {
        // 用户ID
        int userId = StpUtil.getLoginIdAsInt();
        // 设置我发布的约起
        List<MyAgreement> myAgreementList = new ArrayList<>();
        for (Integer type : Arrays.asList(APPOINTMENT.getType(), ACTIVITY.getType(), COMPETITION.getType(), HELP.getType())) {
            List<AgreementAllVO> agreements = agreementService.listAgreementVO(
                    ConditionDTO.builder().type(type).entityId(userId).build());
            myAgreementList.add(MyAgreement.builder().type(type).list(agreements).build());
        }
        return myAgreementList;
    }

    @Override
    public List<MyAgreement> getTakeAgreement() {
        // 用户ID
        int userId = StpUtil.getLoginIdAsInt();
        List<MyAgreement> myTakeAgreementList = new ArrayList<>();
        Map<Integer, List<AgreementAllVO>> map = new HashMap<>();
        agreementUserMapper.selectList(
                new LambdaQueryWrapper<BAgreementUser>()
                        .select(BAgreementUser::getAgreementId, BAgreementUser::getType)
                        .eq(BAgreementUser::getUserId, userId)
        ).forEach(item -> {
            AgreementInfoAllVO agreementInfo = agreementService.editAgreement(
                    AgreementDTO.builder()
                            .id(item.getAgreementId())
                            .agreementType(item.getType()).build());
            map.computeIfAbsent(item.getType(), k -> new ArrayList<>())
                    .add(BeanCopyUtils.copyBean(agreementInfo, AgreementAllVO.class));
        });
        map.forEach((type, agreements) -> {
            myTakeAgreementList.add(MyAgreement.builder().type(type).list(agreements).build());
        });
        return myTakeAgreementList;
    }

    @Override
    public List<RecordVO> getIssueRecord() {
        // 用户ID
        int userId = StpUtil.getLoginIdAsInt();
        return recordService.listRecordVO(ConditionDTO.builder().isDelete(0).entityId(userId).build());
    }

    @Override
    public List<QuizVO> getIssueQuiz() {
        // 用户ID
        int userId = StpUtil.getLoginIdAsInt();
        return quizService.listQuizVO(ConditionDTO.builder().entityId(userId).build());
    }

    @Override
    public List<RecordInfoVO> getCollectRecord() {
        // 用户Id
        int userId = StpUtil.getLoginIdAsInt();
        // 获取用户收藏的记录id列表
        List<Integer> recordIds = userCollectMapper.selectList(new LambdaQueryWrapper<BUserCollect>()
                        .select(BUserCollect::getTypeId)
                        .eq(BUserCollect::getType, RECORD.getType())
                        .eq(BUserCollect::getUserId, userId))
                .stream().map(BUserCollect::getTypeId).toList();
        List<RecordInfoVO> list = new ArrayList<>();
        // 遍历获取记录信息
        recordIds.forEach(item -> list.add(recordService.editRecord(item)));
        return list;
    }

    @Override
    public List<QuizInfoVO> getCollectQuiz() {
        // 用户Id
        int userId = StpUtil.getLoginIdAsInt();
        // 获取用户收藏的记录id列表
        List<Integer> quizIds = userCollectMapper.selectList(new LambdaQueryWrapper<BUserCollect>()
                        .select(BUserCollect::getTypeId)
                        .eq(BUserCollect::getType, QUIZ.getType())
                        .eq(BUserCollect::getUserId, userId))
                .stream().map(BUserCollect::getTypeId).toList();
        List<QuizInfoVO> list = new ArrayList<>();
        // 遍历获取记录信息
        quizIds.forEach(item -> list.add(quizService.editQuiz(item)));
        return list;
    }

    @Override
    public List<RecordInfoVO> getLikeRecord() {
        // 用户Id
        int userId = StpUtil.getLoginIdAsInt();
        // 获取用户点赞的记录id列表
        List<Integer> recordIds = getLikeRecordIds(userId);
        List<RecordInfoVO> list = new ArrayList<>();
        recordIds.forEach(item -> list.add(recordService.editRecord(item)));
        return list;
    }

    @Override
    public List<QuizInfoVO> getLikeQuiz() {
        // 用户Id
        int userId = StpUtil.getLoginIdAsInt();
        // 获取用户点赞的题目id列表
        List<Integer> quizIds = getLikeQuizIds(userId);
        List<QuizInfoVO> list = new ArrayList<>();
        quizIds.forEach(item -> list.add(quizService.editQuiz(item)));
        return list;
    }

    @Override
    public List<CommentVO> getLikeComment() {
        // 用户Id
        int userId = StpUtil.getLoginIdAsInt();
        // 获取用户点赞的评论id列表
        List<Integer> commentIds = getLikeCommentIds(userId);
        return BeanCopyUtils.copyBeanList(commentMapper.selectBatchIds(commentIds), CommentVO.class);
    }

    @Override
    public void updateUser(UserDTO user) {
        // 更新用户信息
        BUser newUser = BUser.builder()
                .id(user.getId())
                .nickname(user.getNickname())
                .build();
        baseMapper.updateById(newUser);
    }

    @Override
    public void updateUserStatus(DisableDTO disable) {
        // 更新用户状态
        BUser newUser = BUser.builder()
                .id(disable.getId())
                .isDisable(disable.getIsDisable())
                .build();
        userMapper.updateById(newUser);
        if (disable.getIsDisable().equals(TRUE)) {
            // 先踢下线
            StpUtil.logout(disable.getId());
            // 再封禁账号
            StpUtil.disable(disable.getId(), 86400);
        } else {
            // 解除封禁
            StpUtil.untieDisable(disable.getId());
        }
    }

    @Override
    public List<OnlineUserVO> listOnlineUser(ConditionDTO condition) {
        // 查询所有会话token
        List<String> tokenList = StpUtil.searchTokenSessionId("", 0, -1, false);
        return tokenList.stream()
                .map(token -> {
                    // 获取tokenSession
                    SaSession sessionBySessionId = StpUtil.getSessionBySessionId(token);
                    return (OnlineUserVO) sessionBySessionId.get(ONLINE_USER);
                })
                .filter(onlineVO -> StringUtils.isEmpty(condition.getKeyword()) ||
                        StringUtils.isNotNull(onlineVO) && onlineVO.getNickname().contains(condition.getKeyword()))
                .sorted(Comparator.comparing(OnlineUserVO::getLoginTime).reversed())
                .toList();
    }

    @Override
    public void kickOutUser(String token) {
        StpUtil.logoutByTokenValue(token);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateUserEmail(EmailDTO email) {
        verifyCode(email.getEmail(), email.getCode());
        BUser newUser = BUser.builder()
                .id(StpUtil.getLoginIdAsInt())
                .email(email.getEmail())
                .build();
        userMapper.updateById(newUser);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String updateUserAvatar(MultipartFile file) {
        // 头像上传
        String avatar = uploadStrategyContext.executeUploadStrategy(file, FilePathEnum.AVATAR.getPath());
        // 更新用户头像
        BUser newUser = BUser.builder()
                .id(StpUtil.getLoginIdAsInt())
                .avatar(avatar)
                .build();
        userMapper.updateById(newUser);
        return avatar;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateUserInfo(UserInfoDTO userInfo) {
        BUser newUser = BUser.builder()
                .id(StpUtil.getLoginIdAsInt())
                .nickname(userInfo.getNickname())
                .intro(userInfo.getIntro())
                .grade(userInfo.getGrade())
                .degree(userInfo.getDegree())
                .build();
        userMapper.updateById(newUser);
    }

    @Override
    public void updatePassword(UserPasswdDTO user) {
        verifyCode(user.getUsername(), user.getCode());
        BUser existUser = userMapper.selectOne(new LambdaQueryWrapper<BUser>()
                .select(BUser::getUsername)
                .eq(BUser::getUsername, user.getUsername()));
        Assert.notNull(existUser, "邮箱尚未注册！");
        // 根据用户名修改密码
        userMapper.update(new BUser(), new LambdaUpdateWrapper<BUser>()
                .set(BUser::getPassword, SecurityUtils.sha256Encrypt(user.getPassword()))
                .eq(BUser::getUsername, user.getUsername()));
    }

    @Override
    public void resetPassword(Integer userId) {
        // 用户是否存在
        BUser user = userMapper.selectById(userId);
        Assert.isFalse(StringUtils.isNull(user), "用户不存在！");
        // 根据用户ID重置密码
        userMapper.update(user, new LambdaUpdateWrapper<BUser>()
                .set(BUser::getPassword, SecurityUtils.sha256Encrypt("123456"))
                .eq(BUser::getId, userId));
    }

    /**
     * 校验验证码
     *
     * @param username 用户名
     * @param code     验证码
     */
    private void verifyCode(String username, String code) {
        String sysCode = redisService.getObject(CODE_KEY_R + username);
        Assert.notBlank(sysCode, "验证码未发送或已过期！");
        Assert.isTrue(sysCode.equals(code), "验证码错误，请重新输入！");
    }

    /**
     * 获取用户点赞的记录ids
     */
    private List<Integer> getLikeRecordIds(Integer userId) {
        List<Integer> recordIds = new ArrayList<>(redisService.getSet(USER_RECORD_LIKE + userId)
                .stream().map(item -> Integer.valueOf(item.toString())).toList());
        List<BUserLike> userLikeList = userLikeMapper.selectList(new LambdaQueryWrapper<BUserLike>()
                .select(BUserLike::getTypeId)
                .eq(BUserLike::getType, RECORD.getType())
                .eq(BUserLike::getUserId, userId));
        if (StringUtils.isNotEmpty(userLikeList)) {
            recordIds.addAll(userLikeList.stream().map(BUserLike::getTypeId).toList());
        }
        return recordIds;
    }


    /**
     * 获取用户点赞的题目ids
     */
    private List<Integer> getLikeQuizIds(Integer userId) {
        List<Integer> quizIds = new ArrayList<>(redisService.getSet(USER_QUIZ_LIKE + userId)
                .stream().map(item -> Integer.valueOf(item.toString())).toList());
        List<BUserLike> userLikeList = userLikeMapper.selectList(new LambdaQueryWrapper<BUserLike>()
                .select(BUserLike::getTypeId)
                .eq(BUserLike::getType, QUIZ.getType())
                .eq(BUserLike::getUserId, userId));
        if (StringUtils.isNotEmpty(userLikeList)) {
            quizIds.addAll(userLikeList.stream().map(BUserLike::getTypeId).toList());
        }
        return quizIds;
    }

    /**
     * 获取用户点赞的评论ids
     */
    private List<Integer> getLikeCommentIds(Integer userId) {
        List<Integer> commentIds = new ArrayList<>(redisService.getSet(USER_COMMENT_LIKE + userId)
                .stream().map(item -> Integer.valueOf(item.toString())).toList());
        List<BUserLike> userLikeList = userLikeMapper.selectList(new LambdaQueryWrapper<BUserLike>()
                .select(BUserLike::getTypeId)
                .eq(BUserLike::getType, COMMENT.getType())
                .eq(BUserLike::getUserId, userId));
        if (StringUtils.isNotEmpty(userLikeList)) {
            commentIds.addAll(userLikeList.stream().map(BUserLike::getTypeId).toList());
        }
        return commentIds;
    }

    /**
     * 获取用户参与的约起记录ids信息
     */
    private List<MyIssueAgreement> getIssueAgreement(Integer userId) {
        List<MyIssueAgreement> myIssueAgreementList = new ArrayList<>();
        Map<Integer, List<Integer>> map = new HashMap<>();
        agreementUserMapper.selectList(new LambdaQueryWrapper<BAgreementUser>()
                .select(BAgreementUser::getAgreementId, BAgreementUser::getType)
                .eq(BAgreementUser::getUserId, userId)).forEach(
                item -> map.computeIfAbsent(item.getType(), k -> new ArrayList<>()).add(item.getAgreementId()));
        map.forEach((type, ids) -> myIssueAgreementList.add(MyIssueAgreement.builder().type(type).ids(ids).build()));
        return myIssueAgreementList;
    }

}